﻿using Exceptionless;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Configuration;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading.Tasks;

namespace Helpers
{
    public static class SerilogFactory
    {
        /// <summary>
        /// 初始化serilog默认配置,默认把日志保存到三个文件inf,wrn,err,错误日志默认显示在console
        /// </summary>
        public static void Init(IConfiguration configuration = default, Action<SerilogHelperOptions> configureOptions = default, Action<LoggerConfiguration, SerilogHelperOptions> configureLogger = default, IServiceCollection services = default)
        {
            try
            {
                var options = configuration?.GetSection(SerilogHelperOptions.SectionName).Get<SerilogHelperOptions>() ?? new SerilogHelperOptions();
                if (configureOptions != default)
                {
                    configureOptions(options);
                }

                //设置AppName
                if (services != null)
                {
                    using var p = services.BuildServiceProvider();
                    var hostEvn = p.GetService<IHostEnvironment>();
                    if (hostEvn != null)
                    {
                        options.AppName ??= hostEvn.ApplicationName;
                        options.EnvName ??= hostEvn.EnvironmentName;
                    }
                }
                options.AppName ??= AppDomain.CurrentDomain.FriendlyName;


                //ExceptionlessClient.Default.Configuration.SetUserIdentity(options.UserIdentity);
                if (string.IsNullOrEmpty(options.LogPath))
                {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        options.LogPath = Environment.GetEnvironmentVariable(SerilogHelperOptions.WindowsAppLogPath) ?? options.WindowsLogPath ?? GetLastLogicalDrives();
                        options.LogPath = options.LogPath.TrimEnd('\\') + $"\\logs\\{options.AppName}\\";
                        if (!string.IsNullOrEmpty(options.AppMode))
                        {
                            options.LogPath += $"{options.AppMode}\\";
                        }
                    }
                    else
                    {
                        options.LogPath = @"logs";
                    }
                }

                var conf = new LoggerConfiguration()
                    .Enrich.WithProperty("appName", options.AppName)
                    .Enrich.WithProperty("envName", options.EnvName)
                    .Enrich.WithProperty("hostName", options.ComputerName)
#if DEBUG
                    .MinimumLevel.Verbose()
#else
                    .MinimumLevel.Is(options.MinimumLevel)
#endif
                    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                    .MinimumLevel.Override("Microsoft.Hosting", LogEventLevel.Information)
                    //.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Information)
                    .MinimumLevel.Override("System", LogEventLevel.Warning)
                    .WriteTo.Debug();

                if (!HasConsoleLog(configuration) && (options.IsLogToConsole || Debugger.IsAttached))
                {
                    conf.WriteTo.Console();
                }

                //从配置文件加载
                if (configuration != null) conf.ReadFrom.Configuration(configuration);

                if (options.IsLogToFile)
                {
                    conf.WriteTo.Logger(fileLogger =>
                        {
                            fileLogger
                            .Filter.ByIncludingOnly(xx =>
                            {
                                return xx.Level > LogEventLevel.Warning;
                            })
                            .WriteTo.Async(x =>
                            {
                                SetupFile(options, x, LogEventLevel.Error);
                            });

                            if (!HasConsoleLog(configuration) && !options.IsLogToConsole && !Debugger.IsAttached)
                            {
                                fileLogger.WriteTo.Console();
                            }
                        })
                        .WriteTo.Logger(fileLogger =>
                        {
                            fileLogger
                            .Filter.ByIncludingOnly(xx =>
                            {
                                return xx.Level == LogEventLevel.Warning;
                            })
                            .WriteTo.Async(x =>
                            {
                                SetupFile(options, x, LogEventLevel.Warning);
                            });
                        })
                        .WriteTo.Logger(fileLogger =>
                        {
                            fileLogger
                            .Filter.ByIncludingOnly(xx => xx.Level < LogEventLevel.Warning)
                            .WriteTo.Async(x =>
                            {
                                SetupFile(options, x, LogEventLevel.Information);
                            });
                        });
                }

                configureLogger?.Invoke(conf, options);

                Log.Logger = conf.CreateLogger();

                //记录全局未捕获异常
                if (options.IsLogUnhandledException)
                {
                    AppDomain.CurrentDomain.UnhandledException += options.UnhandledException;
                    TaskScheduler.UnobservedTaskException += options.UnobservedTaskException;
                }

                //程序退出时触发
                AppDomain.CurrentDomain.ProcessExit += (_, __) =>
                {
                    Log.CloseAndFlush();
                };
                Log.Debug($"serilog组件初始化完成,{options.AppName},{options.EnvName},{(options.IsLogToFile ? " 日志文件路径--" + options.LogPath : "")}");
            }
            catch (Exception ex)
            {
                Console.WriteLine("serilog组件初始化异常" + ex.ToString());
            }
        }

        private static void SetupFile(SerilogHelperOptions options, LoggerSinkConfiguration configuration, LogEventLevel level)
        {
            string s;
            switch (level)
            {
                case LogEventLevel.Verbose:
                case LogEventLevel.Debug:
                case LogEventLevel.Information:
                    s = "inf";
                    break;
                case LogEventLevel.Warning:
                    s = "wrn";
                    break;
                case LogEventLevel.Error:
                case LogEventLevel.Fatal:
                default:
                    s = "err";
                    break;
            }
            configuration.File(Path.Combine(options.LogPath, $"log-{options.ComputerName}-{Process.GetCurrentProcess().Id}-.{s}"),
                rollingInterval: RollingInterval.Day,
                retainedFileCountLimit: options.RetainedFileCountLimit,
                retainedFileTimeLimit: options.RetainedFileTimeLimit,
                outputTemplate: options.FileOutputTemplate);
        }

        /// <summary>
        /// 获取最后一个磁盘
        /// </summary>
        /// <returns></returns>
        public static string GetLastLogicalDrives()
        {
            Console.WriteLine("LogicalDrives " + string.Join(";", Directory.GetLogicalDrives()));
            return Directory.GetLogicalDrives().Last();
        }

        public static void ToLog(string msg)
        {
            try
            {
                File.AppendAllText(@"c:\1.txt", $"{DateTime.Now} {msg} {Environment.NewLine}");
            }
            catch (Exception)
            {
            }
            try
            {
                File.AppendAllText(@"z:\1.txt", $"{DateTime.Now} {msg} {Environment.NewLine}");
            }
            catch (Exception)
            {
            }
            try
            {
                File.AppendAllText(@"\\9d11c4b348-rmw75.cn-zhangjiakou.nas.aliyuncs.com\myshare\2.txt", $"{DateTime.Now} {msg} {Environment.NewLine}");
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// 判断是否需要添加Console
        /// </summary>
        private static bool HasConsoleLog(IConfiguration configuration)
        {
            var index = 0;
            do
            {
                var name = configuration?[$"Serilog:WriteTo:{index++}:Name"];
                if (string.IsNullOrEmpty(name))
                {
                    break;
                }
                if (name.Equals("console", StringComparison.OrdinalIgnoreCase))
                {
                    index = -1;
                    break;
                }
            } while (true);
            return index == -1;
        }
    }
}
